#include "vtkFunctions.hpp"
#include "vtkFunctions_export.hpp"
#include "vtkConstants.hpp"

namespace hms::vtk
{



namespace internal
{

template<typename MeshType, Uniformity isUniform>
void writeSourceField(
	xml::XMLElement* fieldElem,
	const SourceField<isUniform, MeshType>& sField
){
	// using S = SourceField<isUniform, MeshType>;
	using U = UpdateMethod;
	U updateMethod { sField.updateMethod() };

	fieldElem->SetAttribute( nComponentsName, sField.nComponents() );
	fieldElem->SetAttribute( "isUniform", static_cast<bool>(isUniform) );
	fieldElem->SetAttribute( "updateMethod",
		std::string{ magic_enum::enum_name( updateMethod ) }.c_str()
	);
	/* for calculated source fields, such as infiltration, etc., no values are
	 * written, because no values need to be read either, as they are
	 * calculated instead */
	switch ( updateMethod ){
		case U::none:
			if constexpr (isUniform)
				matrixToText( fieldElem,sField.storage());
			else
				matrixToText( fieldElem,sField.domainValues());
			break;
		case U::timeSeries: {
			fieldElem->SetAttribute( "useInterpolation",
				sField.useInterpolation()
			);
			hms::vtk::writeTimeSeries<isUniform>(
				fieldElem, sField.timeSeries(), sField.mesh()
			);
			break;
		}
		case U::function:
			writeFunctions( fieldElem, sField.functions() );
			break;
		case U::callable:
			break;
	}
}

} // namespace internal


template<typename SolverType>
void writeSourceField(
	xml::XMLElement* parent,
	const SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::SourceFieldName fieldName
){
	using S = SolverBase<SolverType>;
	using MeshType = typename S::MeshType;
	const SourceFieldVar<MeshType>& fieldVar { solver.sourceField(fieldName) };

	xml::XMLElement* fieldElem { parent->InsertNewChildElement(sourceChild) };
	fieldElem->SetAttribute("Name",
		std::string{ magic_enum::enum_name(fieldName) }.c_str()
	);
	
	std::visit(
		[&](const auto& sField){
			internal::writeSourceField( fieldElem, sField );
		},
		fieldVar
	);
}

template<typename SolverType>
void writeSourceFields(
	xml::XMLNode* node,
	const SolverBase<SolverType>& solver
){
	using S = SolverBase<SolverType>;
	using SourceFieldName = typename S::SourceFieldName;

	xml::XMLElement* sFields { createRootOrInsert(node, sourceNode) };

	for ( int i {0}; i < S::nSourceFields; ++i ){
		SourceFieldName name { static_cast<SourceFieldName>(i) };
		if ( solver.useSourceField(name) )
			writeSourceField(
				sFields, solver, name
			);
	}
}

template<typename SolverType>
std::unique_ptr<xml::XMLDocument> sourceFieldsToXML(
	const SolverBase<SolverType>& solver
){
	auto doc { std::make_unique<xml::XMLDocument>() };
	writeSourceFields( doc.get(), solver );
	return doc;
}


} // namespace hms::vtk
